/*
 * commons is a based project implemented
 * Copyright (C) 2024 Jasmine
 */
package com.commons.database.base;

import java.io.Serializable;
import java.lang.annotation.Annotation;
import java.lang.reflect.Field;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Objects;
import java.util.function.Consumer;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.CollectionUtils;

import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.core.toolkit.reflect.GenericTypeUtils;
import com.baomidou.mybatisplus.extension.toolkit.Db;
import com.commons.core.anno.Query;
import com.commons.core.anno.QueryIgnore;
import com.commons.core.domain.dto.BaseDTO;
import com.commons.core.enums.ExpressionEnum;
import com.commons.core.exception.GlobalResultInfoEnum;
import com.commons.core.utils.BeanCopyUtil;
import com.commons.core.utils.ColumnUtil;
import com.commons.core.utils.ExceptionUtil;
import com.commons.database.utils.MyBatisPlusUtil;
import com.commons.uid.UidGenerator;
import com.github.pagehelper.PageHelper;
import com.github.pagehelper.PageInfo;
import com.google.common.collect.Lists;

/**
 * @author Jasmine
 * @version 1.0
 * @date 2020-09-25 14:33:33
 * @see BaseServiceImpl
 * @since JDK1.8 M Mapper文件 T 实体类 D DTO入参 V VO出参
 * @param <M> Mapper 接口
 * @param <T> 实体类
 * @param <D> DTO入参
 * @param <V> VO出参
 */
public abstract class BaseServiceImpl<M extends BaseMapper<T>, T, D extends BaseDTO, V>
    implements BaseService<T, D, V> {
    private static final Logger log = LoggerFactory.getLogger(BaseServiceImpl.class);

    @Autowired
    protected M baseMapper;

    protected final Class<?>[] typeArguments = GenericTypeUtils.resolveTypeArguments(getClass(), BaseServiceImpl.class);
    protected final Class<T> entityClass = this.currentEntityClass();
    protected final Class<V> voClass = this.currentVOClass();
    protected final Class<D> dtoClass = this.currentDTOClass();

    private final List<Field> entityFields = ColumnUtil.getAllFieldsArray(entityClass);
    private final List<Field> dtoFields = ColumnUtil.getAllFieldsArray(dtoClass);

    /**
     * 按每500个一组分割，一次批量插入过多会报错
     */
    private static final Integer DEFAULT_BATCH_SIZE = 500;

    @Autowired
    private UidGenerator uidGenerator;

    @Override
    public QueryWrapper<T> getWrapper() {
        return Wrappers.query();
    }

    /**
     * 插入一条记录（选择字段，策略插入）
     *
     * @param modelDTO DTO对象
     */
    @Override
    public int add(D modelDTO) {
        T entity = BeanCopyUtil.copyProperty(modelDTO, entityClass);
        // 设置主键
        setKey(entity);
        int count = baseMapper.insert(entity);
        if (count <= 0) {
            ExceptionUtil.send(GlobalResultInfoEnum.FAILED);
        }
        return count;
    }

    /**
     * 根据 ID 查询
     *
     * @param id 主键ID
     */
    @Override
    public V get(Serializable id) {
        T entity = baseMapper.selectById(id);
        if (Objects.isNull(entity)) {
            return null;
        }
        return BeanCopyUtil.copyProperty(entity, voClass);
    }

    /**
     * 列表查询
     *
     * @param modelDTO 实体对象封装类
     */
    @Override
    public List<V> list(D modelDTO) {
        List<T> list = selectList(modelDTO);
        if (CollectionUtils.isEmpty(list)) {
            return new ArrayList<>();
        }
        return BeanCopyUtil.copyListProperties(list, voClass);
    }

    /**
     * 分页查询
     *
     * @param modelDTO 实体对象封装类
     */
    @Override
    public PageInfo<V> page(D modelDTO) {
        PageInfo<V> pageInfo = PageHelper.startPage(modelDTO.getPageNum(), modelDTO.getPageSize())
            .doSelectPageInfo(() -> selectList(modelDTO));
        if (!CollectionUtils.isEmpty(pageInfo.getList())) {
            List<V> listVO = BeanCopyUtil.copyListProperties(pageInfo.getList(), voClass);
            pageInfo.setList(listVO);
        }
        return pageInfo;
    }

    /**
     * 插入一条记录（选择字段，策略插入）
     *
     * @param modelDTO DTO对象
     */
    @Override
    public boolean update(D modelDTO) {
        T entity = BeanCopyUtil.copyProperty(modelDTO, entityClass);
        int i = baseMapper.updateById(entity);
        if (i <= 0) {
            ExceptionUtil.send(GlobalResultInfoEnum.FAILED);
        }
        return true;
    }

    /**
     * 删除一条记录
     *
     * @param id ID主键
     */
    @Override
    public boolean delete(Serializable id) {
        int i = baseMapper.deleteById(id);
        if (i <= 0) {
            ExceptionUtil.send(GlobalResultInfoEnum.FAILED);
        }
        return true;
    }

    @Transactional(rollbackFor = Exception.class)
    @Override
    public boolean saveBatch(List<D> dtoList) {
        if (CollectionUtils.isEmpty(dtoList)) {
            return false;
        }

        // 判断最大值，做批量处理，数据太多insertList报错
        // 总记录数
        int total = dtoList.size();

        List<T> entityList;
        // 如果未超过MAX_NUMBER=500，直接插入
        if (dtoList.size() <= DEFAULT_BATCH_SIZE) {
            entityList = new ArrayList<>();
            dtoList.forEach(d -> entityList.add(BeanCopyUtil.copyProperty(d, entityClass)));
            // 设置主键ID
            entityList.forEach(p -> setKey(p));
            return Db.saveBatch(entityList, total);
        }

        // 超过MAX_NUMBER=500，需要分批插入
        List<List<D>> listList = Lists.partition(dtoList, DEFAULT_BATCH_SIZE);
        entityList = new ArrayList<>();
        for (List<D> list : listList) {
            entityList.add(BeanCopyUtil.copyProperty(list, entityClass));
            // 设置主键ID
            entityList.forEach(p -> setKey(p));
            Db.saveBatch(entityList, entityList.size());
            entityList.clear();
        }
        return true;
    }

    /**
     * 通过条件查询
     *
     * @param modelDTO 条件
     * @return List<T> 实体列表
     */
    public List<T> selectList(D modelDTO) {
        QueryWrapper<T> queryWrapper = buildQueryWrapper(modelDTO);
        return baseMapper.selectList(queryWrapper);
    }

    /**
     * 主键赋值
     *
     * @param t 实体类
     */
    public void setKey(T t) {
        for (Field field : entityFields) {
            Annotation id = field.getAnnotation(TableId.class);
            if (id == null) {
                continue;
            }
            try {
                field.setAccessible(true);
                Object o = field.get(t);
                // 解决由于Feign远程调用序列化问题将一些类型修改默认值的bug，比如Long为null，就转为0
                if (Objects.nonNull(o) && !Objects.equals(o, 0L)) {
                    return;
                }
                field.set(t, uidGenerator.getUID());
            } catch (IllegalAccessException e) {
                log.error("主键赋值异常");
            }
        }
    }

    public QueryWrapper<T> buildQueryWrapper(D modelDTO) {
        QueryWrapper<T> queryWrapper = new QueryWrapper<>();

        // 设置排序
        MyBatisPlusUtil.setOrder(queryWrapper, modelDTO.getOrders(), entityFields);

        // 解析并拼接查询条件
        for (Field field : dtoFields) {
            List<Consumer<QueryWrapper<T>>> consumers = buildWrapperConsumer(queryWrapper, field);
            queryWrapper.and(CollectionUtils.isEmpty(consumers), q -> consumers.forEach(q::or));
        }
        return queryWrapper;
    }

    /**
     * 构建 QueryWrapper Consumer
     *
     * @param query 查询条件
     * @param field 查询条件字段
     * @param <D> 查询条件数据类型
     * @param <T> 查询数据类型
     * @return QueryWrapper Consumer
     */
    public static <D, T> List<Consumer<QueryWrapper<T>>> buildWrapperConsumer(D query, Field field) {
        boolean accessible = field.canAccess(query);
        List<Consumer<QueryWrapper<T>>> consumers = new ArrayList<>();

        try {
            field.setAccessible(true);
            // 如果字段值为空，直接返回
            Object fieldValue = field.get(query);
            if (Objects.isNull(fieldValue)) {
                return Collections.emptyList();
            }

            // 设置了 @QueryIgnore 注解，直接忽略
            QueryIgnore queryIgnore = field.getAnnotation(QueryIgnore.class);
            if (null != queryIgnore && queryIgnore.value()) {
                return Collections.emptyList();
            }

            // 建议：数据库表列建议采用下划线连接法命名，程序变量建议采用驼峰法命名
            String fieldName = field.getName();
            // 没有 @Query 注解，默认等值查询
            Query queryAnnotation = field.getAnnotation(Query.class);
            if (Objects.isNull(queryAnnotation)) {
                consumers.add(q -> q.eq(ColumnUtil.camelToUnderline(fieldName), fieldValue));
                return consumers;
            }

            // 解析单列查询
            ExpressionEnum queryType = queryAnnotation.expression();
            String[] columns = queryAnnotation.columns();
            final int columnLength = columns.length;
            if (columnLength <= 1) {
                String columnName = columnLength == 1 ? ColumnUtil.camelToUnderline(columns[0])
                    : ColumnUtil.camelToUnderline(fieldName);
                parse(queryType, columnName, fieldValue, consumers);
                return consumers;
            }
            // 解析多列查询
            for (String column : columns) {
                parse(queryType, column, fieldValue, consumers);
            }
        } catch (Exception e) {
            log.error("Build query wrapper occurred an error: {}. Query: {}, Field: {}.", e.getMessage(), query, field,
                e);
        } finally {
            field.setAccessible(accessible);
        }
        return consumers;
    }

    /**
     * 解析查询条件
     *
     * @param queryType 查询类型
     * @param columnName 列名
     * @param fieldValue 字段值
     * @param <T> 查询数据类型
     */
    private static <T> void parse(ExpressionEnum queryType, String columnName, Object fieldValue,
        List<Consumer<QueryWrapper<T>>> consumers) {
        switch (queryType) {
            case EQ -> consumers.add(q -> q.eq(columnName, fieldValue));
            case LIKE -> consumers.add(q -> q.like(columnName, fieldValue));
            case LIKE_LEFT -> consumers.add(q -> q.likeLeft(columnName, fieldValue));
            case LIKE_RIGHT -> consumers.add(q -> q.likeRight(columnName, fieldValue));
            case IN -> consumers.add(q -> q.in(columnName, (List<Object>)fieldValue));
            case NE -> consumers.add(q -> q.ne(columnName, fieldValue));
            case GT -> consumers.add(q -> q.gt(columnName, fieldValue));
            case GE -> consumers.add(q -> q.ge(columnName, fieldValue));
            case LE -> consumers.add(q -> q.le(columnName, fieldValue));
            case LT -> consumers.add(q -> q.lt(columnName, fieldValue));
            case NOT_IN -> consumers.add(q -> q.notIn(columnName, (List<Object>)fieldValue));
            case IS_NULL -> consumers.add(q -> q.isNull(columnName));
            case NOT_NULL -> consumers.add(q -> q.isNotNull(columnName));
            case BETWEEN -> {
                List<Object> between = new ArrayList<>((List<Object>)fieldValue);
                ExceptionUtil.unTrue(between.size() != 2, "[" + columnName + "]" + "必须是一个范围");
                consumers.add(q -> q.between(columnName, between.get(0), between.get(1)));
            }
            default -> throw new IllegalArgumentException(String.format("暂不支持 [%s] 查询类型", queryType));
        }
    }

    /**
     * 获取当前类的第一个泛型参数class
     */
    protected Class<T> currentEntityClass() {
        return (Class<T>)this.typeArguments[1];
    }

    /**
     * 获取当前查询类型
     *
     * @return 当前查询类型
     */
    protected Class<D> currentDTOClass() {
        return (Class<D>)this.typeArguments[2];
    }

    /**
     * 获取当前类的第三个泛型参数class
     */
    protected Class<V> currentVOClass() {
        return (Class<V>)this.typeArguments[3];
        // return (Class<V>)ReflectionKit.getSuperClassGenericType(getClass(), BaseServiceImpl.class, 3);
    }
}
